package handler

import (
	"net/http"
	"strings"
	"time"
	"project-info/src/model"

	"project-info/src/lib")

// TokenRevocation 令牌撤销端点
// POST /oidc/revoke
func TokenRevocation(c *lib.GinContext) (err error) {
	// 获取要撤销的令牌
	token := c.PostForm("token")
	if token == "" {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{
			"error":             "invalid_request",
			"error_description": "Missing token parameter",
		})
	}

	// 获取令牌类型提示
	tokenTypeHint := c.PostForm("token_type_hint")

	// 客户端认证
	clientID := c.PostForm("client_id")
	clientSecret := c.PostForm("client_secret")

	// 也可以通过Authorization头进行客户端认证
	if clientID == "" {
		if authHeader := c.Request().Header.Get("Authorization"); authHeader != "" {
			if strings.HasPrefix(authHeader, "Basic ") {
				// 这里应该解析Basic认证，简化实现
				clientID = "basic_client" // 示例
			}
		}
	}

	if clientID == "" {
		return c.JSON(http.StatusUnauthorized, map[string]interface{}{
			"error":             "invalid_client",
			"error_description": "Client authentication required",
		})
	}

	// 验证客户端
	db := model.DB()
	var oidcClient model.OIDCClient
	if err := db.Where("client_id = ? AND deleted = 0", clientID).First(&oidcClient).Error; err != nil {
		return c.JSON(http.StatusUnauthorized, map[string]interface{}{
			"error":             "invalid_client",
			"error_description": "Invalid client credentials",
		})
	}

	// 验证客户端密钥（如果提供）
	if clientSecret != "" && oidcClient.ClientSecret != clientSecret {
		return c.JSON(http.StatusUnauthorized, map[string]interface{}{
			"error":             "invalid_client",
			"error_description": "Invalid client credentials",
		})
	}

	// 撤销令牌的标志
	revoked := false

	// 根据令牌类型提示优先处理
	switch tokenTypeHint {
	case "access_token":
		if revokeAccessToken(token, clientID) {
			revoked = true
		}
	case "refresh_token":
		if revokeRefreshToken(token, clientID) {
			revoked = true
		}
	default:
		// 没有提示或提示无效，尝试所有类型
		if revokeAccessToken(token, clientID) {
			revoked = true
		} else if revokeRefreshToken(token, clientID) {
			revoked = true
		}
	}

	// RFC 7009: 撤销端点应该返回200状态码，无论令牌是否存在
	// 这是为了防止令牌枚举攻击
	return c.JSON(http.StatusOK, map[string]interface{}{
		"revoked": revoked,
	})
}

// revokeAccessToken 撤销访问令牌
func revokeAccessToken(token, clientID string) bool {
	db := model.DB()
	var accessToken model.OIDCAccessToken
	if err := db.Where("token = ? AND deleted = 0", token).First(&accessToken).Error; err != nil {
		return false
	}

	// 验证令牌是否属于该客户端
	if accessToken.ClientID != clientID {
		return false
	}

	// 软删除访问令牌
	accessToken.Deleted = 1
	now := time.Now()
	accessToken.UpdateTime = &now
	db.Save(&accessToken)

	// 查找并撤销相关的刷新令牌
	db.Model(&model.OIDCRefreshToken{}).Where("access_token_id = ?", accessToken.Id).Update("deleted", 1)

	return true
}

// revokeRefreshToken 撤销刷新令牌
func revokeRefreshToken(token, clientID string) bool {
	db := model.DB()
	var refreshToken model.OIDCRefreshToken
	if err := db.Where("token = ? AND deleted = 0", token).First(&refreshToken).Error; err != nil {
		return false
	}

	// 验证令牌是否属于该客户端
	if refreshToken.ClientID != clientID {
		return false
	}

	// 撤销相关的访问令牌
	db.Model(&model.OIDCAccessToken{}).Where("id = ?", refreshToken.AccessTokenID).Update("deleted", 1)

	// 软删除刷新令牌
	refreshToken.Deleted = 1
	now := time.Now()
	refreshToken.UpdateTime = &now
	db.Save(&refreshToken)

	return true
}

// Logout 登出端点
// GET/POST /oidc/logout
func Logout(c *lib.GinContext) (err error) {
	// 获取参数
	idTokenHint := c.QueryParam("id_token_hint")
	if idTokenHint == "" {
		idTokenHint = c.PostForm("id_token_hint")
	}

	postLogoutRedirectURI := c.QueryParam("post_logout_redirect_uri")
	if postLogoutRedirectURI == "" {
		postLogoutRedirectURI = c.PostForm("post_logout_redirect_uri")
	}

	state := c.QueryParam("state")
	if state == "" {
		state = c.PostForm("state")
	}

	// 如果提供了ID令牌提示，验证并提取信息
	var clientID string
	var userID interface{}
	if idTokenHint != "" {
		if claims, err := parseJWTToken(idTokenHint); err == nil {
			clientID = claims["aud"].(string)
			if sub, ok := claims["sub"].(string); ok {
				// 将字符串转换为用户ID
				if sub == "1" {
					userID = 1
				} else if sub == "2" {
					userID = 2
				}
			}
		}
	}

	// 撤销用户的所有令牌（如果能确定用户）
	if userID != nil {
		revokeAllUserTokens(userID)
	}

	// 清除用户会话
	clearUserSessions(userID)

	// 清除会话cookie
	c.SetCookie(
		"oidc_session",
		"",
		-1,
		"/",
		"",
		false,
		true,
	)

	// 如果提供了登出后重定向URI，验证并重定向
	if postLogoutRedirectURI != "" {
		// 验证重定向URI是否在客户端的允许列表中
		if clientID != "" {
			var client model.OIDCClient
			db := model.DB()
			if err := db.Where("client_id = ? AND deleted = 0", clientID).First(&client).Error; err == nil {
				redirectURIs := []string(client.RedirectURIs)
				// 简化验证：检查是否为已注册的重定向URI的变体
				valid := false
				for _, uri := range redirectURIs {
					// 简单的域名匹配检查
					if strings.Contains(postLogoutRedirectURI, extractDomain(uri)) {
						valid = true
						break
					}
				}
				if valid {
					redirectURL := postLogoutRedirectURI
					if state != "" {
						if strings.Contains(redirectURL, "?") {
							redirectURL += "&state=" + state
						} else {
							redirectURL += "?state=" + state
						}
					}
					c.Redirect(http.StatusFound, redirectURL)
	return nil
				}
			}
		}
	}

	// 显示登出确认页面
	return showLogoutPage(c)
}

// revokeAllUserTokens 撤销用户的所有令牌
func revokeAllUserTokens(userID interface{}) {
	db := model.DB()
	now := time.Now()
	
	// 软删除所有访问令牌
	db.Model(&model.OIDCAccessToken{}).Where("user_id = ? AND deleted = 0", userID).Updates(map[string]interface{}{
		"deleted":     1,
		"revoked":     1,
		"update_time": now,
	})

	// 软删除所有刷新令牌
	db.Model(&model.OIDCRefreshToken{}).Where("user_id = ? AND deleted = 0", userID).Updates(map[string]interface{}{
		"deleted":     1,
		"revoked":     1,
		"update_time": now,
	})
}

// clearUserSessions 清除用户会话
func clearUserSessions(userID interface{}) {
	if userID == nil {
		return
	}

	db := model.DB()
	now := time.Now()
	
	// 软删除所有该用户的会话
	db.Model(&model.OIDCUserSession{}).Where("user_id = ? AND deleted = 0", userID).Updates(map[string]interface{}{
		"deleted":     1,
		"update_time": now,
	})
}

// extractDomain 从URL中提取域名
func extractDomain(url string) string {
	// 简化实现
	if strings.HasPrefix(url, "http://") {
		url = url[7:]
	} else if strings.HasPrefix(url, "https://") {
		url = url[8:]
	}

	if idx := strings.Index(url, "/"); idx != -1 {
		url = url[:idx]
	}

	if idx := strings.Index(url, ":"); idx != -1 {
		url = url[:idx]
	}

	return url
}

// showLogoutPage 显示登出页面
func showLogoutPage(c *lib.GinContext) error {
	logoutHTML := `
<!DOCTYPE html>
<html>
<head>
    <title>登出成功</title>
    <meta charset="UTF-8">
    <style>
        body { 
            font-family: Arial, sans-serif; 
            max-width: 400px; 
            margin: 100px auto; 
            padding: 20px; 
            text-align: center;
        }
        .success-message {
            background-color: #d4edda;
            color: #155724;
            padding: 20px;
            border-radius: 4px;
            border: 1px solid #c3e6cb;
            margin-bottom: 20px;
        }
        .back-link {
            display: inline-block;
            padding: 10px 20px;
            background-color: #007bff;
            color: white;
            text-decoration: none;
            border-radius: 4px;
            margin-top: 10px;
        }
        .back-link:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <div class="success-message">
        <h3>登出成功</h3>
        <p>您已成功登出系统。所有相关的令牌和会话已被清除。</p>
    </div>
    <a href="/" class="back-link">返回首页</a>
</body>
</html>
`

	c.Response().Header().Set("Content-Type", "text/html; charset=utf-8")
	c.HTML(http.StatusOK, logoutHTML, nil)
	return nil
}